#include "stdafx.h"
#include "CScrLanguage.h"
#include "CTokens.h"

char				COperators::s_nNumOperators;
COperator		*	COperators::s_pOperators[];

bool COperators::LoadFromFile(const char *szFilePath)
{
	char *szStr, *szOpcode;
	DWORD opcode;
	if(FILE *hFile = OpenFile(szFilePath, "rt"))
	{
		try
		{
			while(szStr = GetConfigLine(hFile))
			{
				szOpcode = szStr;
				while(!IsSpace(*szStr)) szStr++;
				*szStr++ = '\0';
				while(IsSpace(*szStr)) szStr++;

				if(*szStr && sscanf(szOpcode, "%4x", &opcode) && opcode < MAX_OPCODES)
				{
					bool bIsInverted = false;
					char *pTmp, szOpName[MAX_COMMAND_NAME];
					CCommand * LCommand = nullptr, * RCommand = nullptr;
					if(bIsInverted = szStr[0] == '~') szStr++;

					const CType * param1 = nullptr;

					pTmp = szStr;
				
					while(*pTmp && IsAlphabetic(*pTmp)) pTmp++;
				
					if(*pTmp == '(')
					{
						*pTmp = 0;
						if(LCommand = CCommands::ParseCommandDeclaration(opcode, szStr))
						{
							pTmp++[0] = 0;
							szncpy(szOpName, szStr, MAX_COMMAND_NAME);
							szStr = pTmp + 1;
							pTmp = CCommands::ParseCommandArgList(LCommand, pTmp);
						}
						while(*pTmp != ')') pTmp++;
					}
					else param1 = CSyntax::GetTypeByID(szStr)->Standardise();
				
					if(param1 && param1->IsAnyVar()) szStr++;
				
					szStr++;

					char op[MAX_OPERATOR_CHARS] = "";
					int i = 0;
					bool bIsCond = false,
						 bIsAssign = false;

					while(*szStr && !IsAlphabetic(*szStr) && !IsDecimal(*szStr) && !IsSpace(*szStr))
					{
						if(i < sizeof(op))
						{
							switch(*szStr)
							{
							case '?':
								if(!bIsCond)
								{
									bIsCond = 1;
									break;
								}
							default:
								op[i++] = *szStr;
							}
						}
						szStr++;
					}

					if(i)
					{
						CToken	* pLTok = nullptr,
								* pRTok = nullptr;
						bool bHasDefault = false;
						int nDefault;
						if(!bIsCond && op[0] == '=' && i == 1) bIsAssign = 1;

						const CType * param2 = nullptr;

						if(IsDecimal(*szStr))
						{
							bHasDefault = true;
							nDefault = StringToInt(szStr);
							while(*szStr && IsDecimal(*szStr)) szStr++;
						}

						pTmp = szStr;
						while(*pTmp && IsAlphabetic(*pTmp)) pTmp++;

						if(*pTmp == '(')
						{
							pTmp++[0] = 0;
							if(RCommand = CCommands::ParseCommandDeclaration(opcode, szStr))
							{
								szncpy(szOpName, szStr, MAX_COMMAND_NAME);
								for(szStr = pTmp; *pTmp && *pTmp != ')'; pTmp++);
								pTmp++[0] = 0;
								CCommands::ParseCommandArgList(RCommand, szStr);
							}
							else while(*pTmp && pTmp[-1] != ')') pTmp++;
							szStr = pTmp;
						}
						else if(*szStr && !bHasDefault)
						{
							if(auto pType = CSyntax::GetTypeByID(szStr))
								param2 = pType->Standardise();
							else
								THROW("Invalid type ID '%s'", szStr);
						}
						else if(bHasDefault)
						{
							param2 = CSyntax::GetType(TYPE_INT);
							pRTok = new CToken(TOKEN_VALUE, szStr);
							pRTok->SetValue(nDefault);
							pRTok->SetValueType(param2);
						}

						auto pOperator = Get(op);
						if(!pOperator) pOperator = Add(op);

						if(!pOperator->IsAssignment()) pOperator->IsAssignment(bIsAssign);
						if(!pOperator->IsConditional()) pOperator->IsConditional(bIsCond);

						if(auto pOperation = pOperator->AddOperation((short)opcode, param1, param2, pLTok, pRTok))
						{
							if(LCommand)
							{
								pOperation->SetLeftCommand(LCommand);
								LCommand->SetOperation(pOperation);
							}
							if(RCommand)
							{
								pOperation->SetRightCommand(RCommand);
								RCommand->SetOperation(pOperation);
							}

							pOperation->SetIsConditional(bIsCond);
							pOperation->SetIsAssignment(bIsAssign);
							pOperation->SetIsInverted(bIsInverted);
						}
					}
				}
			}
			CloseFile(hFile);
			return true;
		}
		catch(const char *e)
		{
			char szError[512];
			szncpy(szError, e, sizeof(szError));
			THROW("'%s' in file '%s'", szError, szFilePath);
		}
	}
	else throw("Failed to open file");
	return false;
}